﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.IO;
//MicroDesigner
using Jc.MicroDesigner.Objects;
using Jc.MicroDesigner.Drawings;

namespace Jc.MicroDesigner.Management
{
    /// <summary>
    /// Класс, кординирующий работу всех компонентов схемы
    /// </summary>
    public class ObjectManager
    {
        /// <summary>
        /// Максимальное количество тактов для запуска
        /// </summary>
        public const int MaxTactCount = 1024;

        /// <summary>
        /// Максимальная длительность такта
        /// </summary>
        public const UInt16 MaxTactTime = 512; 

        #region Private

        List<BaseElement> _Elements = new List<BaseElement>();
        List<ISignalGenerator> _Generators = new List<ISignalGenerator>();
        List<SignalContainer> _Watches = new List<SignalContainer>();

        BackgroundWorker _Runner = new BackgroundWorker();
        onProcessComplete _OnComplete = null;

        string _SchemeName = "";
        string _Description = "";
        string _Author = "";
        string _SchemeFile = "";
        string _AppPath = "";

        int _TactCount = 0;
        int _TicPerTact = 0;
        sbyte _InitialSignal = 0;

        Canvas _SchemeCanvas = new Canvas();

        #endregion

        /// <summary>
        /// Создать схему
        /// </summary>
        public ObjectManager()
        {
            _Runner.DoWork += new DoWorkEventHandler(_Runner_DoWork);
            _Runner.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_Runner_RunWorkerCompleted);
        }

        #region Properties

        /// <summary>
        /// Все элементы схемы
        /// </summary>
        public List<BaseElement> Elements
        {
            get
            {
                return _Elements;
            }
        }

        /// <summary>
        /// Генераторы сигнала
        /// </summary>
        public List<ISignalGenerator> Generators
        {
            get
            {
                return _Generators;
            }
        }

        /// <summary>
        /// Наблюдаемые элементы
        /// </summary>
        public List<SignalContainer> Watches
        {
            get
            {
                return _Watches;
            }
        }

        /// <summary>
        /// Имя схемы
        /// </summary>
        public string SchemeName
        {
            get
            {
                return _SchemeName;
            }
            set
            {
                _SchemeName = value;
            }
        }

        /// <summary>
        /// Описание
        /// </summary>
        public string Description
        {
            get
            {
                return _Description;
            }
            set
            {
                _Description = value;
            }
        }

        /// <summary>
        /// Автор схемы
        /// </summary>
        public string Author
        {
            get
            {
                return _Author;
            }
            set
            {
                _Author = value;
            }
        }

        /// <summary>
        /// Путь к файлу схемы
        /// </summary>
        public string SchemeFile
        {
            get
            {
                return _SchemeFile;
            }
            set
            {
                _SchemeFile = value;
            }
        }

        /// <summary>
        /// Рабочая папка приложения
        /// </summary>
        public string AppPath
        {
            get
            {
                return _AppPath;
            }
            set
            {
                _AppPath = value;
                if (!String.IsNullOrEmpty(_AppPath) && (_AppPath[_AppPath.Length - 1] != Path.DirectorySeparatorChar))
                {
                    _AppPath += Path.DirectorySeparatorChar;
                }
            }
        }

        /// <summary>
        /// Количество тактов запуска
        /// </summary>
        public int TactCount
        {
            get
            {
                return _TactCount;
            }
            set
            {
                _TactCount = value;
            }
        }

        /// <summary>
        /// Время одного такта
        /// </summary>
        public int TicPerTact
        {
            get { return _TicPerTact; }
            set { _TicPerTact = value; }
        }

        /// <summary>
        /// Сигнал по-умолчанию для висящих в воздухе контактов
        /// </summary>
        public sbyte InitialSignal
        {
            get { return _InitialSignal; }
            set { _InitialSignal = value; }
        }

        /// <summary>
        /// Свойства холста
        /// </summary>
        public Canvas SchemeCanvas
        {
            get
            {
                return _SchemeCanvas;
            }
        }

        /// <summary>
        /// Событие по завершению эмуляции
        /// </summary>
        public event onProcessComplete OnEmulateComplete
        {
            add
            {
                _OnComplete += value;
            }
            remove
            {
                _OnComplete -= value;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Проверить имя элемента на уникальность
        /// </summary>
        /// <returns></returns>
        public bool CheckElementName(BaseElement element, string newname)
        {
            int i = 0;
            while ((i < Elements.Count) && !((Elements[i].Name == newname) && (Elements[i] != element)))
                i++;
            if (i < Elements.Count)
                return false;
            else
                return true;
        }

        /// <summary>
        /// Поиск элемента по имени
        /// </summary>
        /// <param name="name">Имя</param>
        /// <returns>Элемент</returns>
        public BaseElement SearchElementByName(string name)
        {
            BaseElement res = null;
            int i = 0;
            while ((i < _Elements.Count) && (_Elements[i].Name != name))
                i++;
            if (i < _Elements.Count)
                res = _Elements[i];
            return res;
        }

        /// <summary>
        /// Подобрать незанятое имя для нового элемента
        /// </summary>
        /// <param name="elementtype">Тип элемента</param>
        /// <returns></returns>
        public string NewElementName(Type elementtype)
        {
            string template = elementtype.Name;
            return NewElementName(template);
        }

        /// <summary>
        /// Подобрать незанятое имя для нового элемента
        /// </summary>
        /// <param name="typename">Тип элемента</param>
        /// <returns>Новое имя</returns>
        public string NewElementName(string typename)
        {
            string template = typename;
            string newname = "";
            bool valid = false;
            int j = 1;
            while (!valid)
            {
                newname = template + "_" + j.ToString();
                int i = 0;
                while ((i < Elements.Count) && !(Elements[i].Name == newname))
                    i++;
                if (i < Elements.Count)
                {
                    valid = false;
                    j++;
                }
                else
                    valid = true;
            }
            return newname;
        }

        /// <summary>
        /// Привести схему в исходное состояние
        /// </summary>
        public void Refresh()
        {
            for (int i = 0; i < _Elements.Count; i++)
                _Elements[i].Refresh();
        }

        /// <summary>
        /// Удалить объект из схемы
        /// </summary>
        /// <param name="element">Объект</param>
        public void DisposeObject(BaseElement element)
        {
            if (element != null)
            {
                if (_Elements.Contains(element))
                {
                    _Elements.Remove(element);
                }
                if (element is CircuitElement)
                {
                    CircuitElement ce = element as CircuitElement;
                    ce.DisconnectAll();
                    //удаление из списка наблюдений
                    for (int p = 0; p < ce.PINs.Count; p++)
                    {
                        int i = 0;
                        while ((i < _Watches.Count) && (_Watches[i].Source != ce.PINs[p]))
                            i++;
                        if (i < _Watches.Count)
                            _Watches.RemoveAt(i);
                    }
                }
                if (element is ISignalGenerator)
                {
                    ISignalGenerator siggen = element as ISignalGenerator;
                    int index = _Generators.IndexOf(siggen);
                    if (index >= 0)
                        _Generators.RemoveAt(index);
                }
            }
        }

        #endregion

        #region Scheme Run

        void _Runner_DoWork(object sender, DoWorkEventArgs e)
        {
            RunScheme();
        }

        void _Runner_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (_OnComplete != null)
                _OnComplete();
        }

        /// <summary>
        /// Запуск выполнения
        /// </summary>
        void RunScheme()
        {
			//Переводим все элементы в начальное состояние
            Refresh();
            // Создаём фильтр с эффективными элементами
            List<CircuitElement> PElements = new List<CircuitElement>();
            // Создаём фильтр с неподключёнными входными пинами для подачи на них Initial Signal
            List<PIN> NotConnectedInputs = new List<PIN>();
            for (int i = 0; i < _Elements.Count; i++)
            {
                if (!(_Elements[i] is Node) && 
                    !(_Elements[i] is Wire) &&
                    (_Elements[i] is CircuitElement) && 
                    (!(_Elements[i] is ISignalGenerator) || (_Elements[i] is CompositeElement)) )
                {
                    CircuitElement ce = _Elements[i] as CircuitElement;
                    PElements.Add(ce);
                    //Просматриваем пины
                    foreach (PIN p in ce.PINs)
                    {
                        if ((p.Type == PINType.In) && (p.FriendPIN == null))
                            NotConnectedInputs.Add(p);
                    }
                }
            }
            //инициализация контейнеров для сбора информации
            for (int watch = 0; watch < _Watches.Count; watch++)
            {
                _Watches[watch].Capacity = _TactCount * _TicPerTact;
            }

            bool[] marks = new bool[PElements.Count];
            bool[] sends = new bool[PElements.Count];

            //цикл генерации сигналов
            for (int tact = 0; tact < _TactCount; tact++)
            {
                for (int i = 0; i < _TicPerTact; i++)
                {
                    //генерируем новые сигналы
                    for (int gi = 0; gi < Generators.Count; gi++)
                        Generators[gi].GenerateSignal(i, tact);
                    //генерируем сигналы на отключенных входных пинах 
                    foreach (PIN in_pin in NotConnectedInputs)
                        in_pin.Recieve(_InitialSignal);
                    //только один эффективный send и perform на каждом такте
                    bool perform = true;
                    for (int n = 0; n < PElements.Count; n++)
                    {
                        marks[n] = false;
                        sends[n] = false;
                    }
                    //пока есть полезная работа, выполняем передачу и обработку сигналов в схеме
                    while (perform)
                    {
                        perform = false;
                        //цикл передачи данных
                        for (int pi = 0; pi < PElements.Count; pi++)
                            if (!sends[pi])
                            {
                                sends[pi] = PElements[pi].SendData(); ;
                            }
                        //цикл выполнения работы
                        for (int pi = 0; pi < PElements.Count; pi++)
                            if (!marks[pi])
                            {
                                marks[pi] = PElements[pi].PerformAction();
                                perform = perform | marks[pi];
                            }
                    }
                    //Секция сбора данных
                    for (int watch = 0; watch < _Watches.Count; watch++)
                    {
                        _Watches[watch].Signals[tact * _TicPerTact + i] = (_Watches[watch].Source as PIN).Signal;
                    }
                }
            }
        }

        /// <summary>
        /// Запуск процесса эмуляции
        /// </summary>
        public void Emulate()
        {
            _Runner.RunWorkerAsync();
        }

        /// <summary>
        /// Прекратить эмуляцию
        /// </summary>
        public void StopEmulate()
        {
            if (_Runner.IsBusy)
                _Runner.CancelAsync();
        }

        #endregion
    }

    /// <summary>
    /// Делегат для событий по завершению эмуляции
    /// </summary>
    public delegate void onProcessComplete();
}